 #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "eval.h"
#include "stack.h"
#include "mm.h"
#include "ops.h"
#include "error.h"

#define IS(x)      if(!strcmp(MMSymbolName(first),_FN(x)))
#define LOC(x)     nth(x,list)

#define GO_GO_STACK()				\
  {						\
    void * value = MMCdr(object);		\
    caller = MMCaller(object);			\
    STACKPush(caller);				\
    while (value != MMNil() &&			\
	   MMIsType(value, CFCONS_TYPE))	\
      {						\
	eval(MMCar(value));			\
	value = MMCdr(value);			\
	MMCallerLength(caller)++;		\
      }						\
  }						\
						\

void eval (void * object)
{
  void * first;
  CFCaller * caller;
  if(object == MMNil() ||
     object == MMT())
    {
      STACKPush(object);
      return;
    }
	
  switch(MMGetType(object))
    {
    case CFINTEGER_TYPE: case CFFLOAT_TYPE: case CFSTRING_TYPE: 
    case CFCHARACTER_TYPE:
      STACKPush(object);
      return;
      break;
			
    case CFCONS_TYPE:
      first = MMCar(object);
      if (!MMIsType(first, CFSYMBOL_TYPE)) {
	ERRORThrow3("Object of type '",
		    _TYPE(MMGetType(first)),
		    "' is not a valid caller.");
      }
			  
      IS(QUIT){
	GO_GO_STACK();
	CFOPQuit(caller);
	break;
      }
      
      IS(ADD){
	GO_GO_STACK();
	CFOPAdd(caller);
	break;
      }
      
      IS(SUB){
	GO_GO_STACK();
	CFOPSub(caller);
	return;
	break;
      }
      IS(TIMES){
	GO_GO_STACK();
	CFOPMul(caller);
	return;
	break;
      }
      IS(DIV){
	GO_GO_STACK();
	CFOPDiv(caller);
	return;
	break;
      }

      IS(EQUAL){
	GO_GO_STACK();
	CFOPEq(caller);
	return;
	break;
      }
      IS(SAME){
	GO_GO_STACK();
	CFOPSame(caller);
	return;
	break;
      }
      IS(NOT){
	GO_GO_STACK();
	CFOPNot(caller);
	return;
	break;
      }
      IS(QUOTE){
	{
	  void * value = MMCdr(object);				
	  if(value == MMNil())
	    ERRORThrow3("'",_FN(QUOTE), "' expects 1 operand");
	  if(MMIsType(value, CFCONS_TYPE))
	    {	
	      STACKPush(MMCar(value));			
	      value = MMCdr(value);			
	    }else
	    ERRORThrow3("'",_FN(QUOTE), "' expects 1 operand in a list");
	  if(value != MMNil())
	    ERRORThrow3("'",_FN(QUOTE), "' expects 1 operand");
	  return;
	  break;
      }						
      }      
      /*EQ("set"){
	      eval_aux(LOC(2));
	      STACKPush(LOC(1));
	      Set();
	      STACKPush(LOC(1));
	      return;
	      break;
	      }
	      EQ("quote"){
	      STACKPush(LOC(1));
	      return;
	      break;
	      }
	      EQ("eval"){
	      eval_aux(LOC(1));
	      eval_aux(STACKPop());
	      return;
	      }
	      EQ("car"){
	      eval_aux(LOC(1));
	      STACKPush(Car(STACKPop()));
	      return;
	      break;
	      }
	      EQ("cdr"){
	      eval_aux(LOC(1));
	      STACKPush(Cdr(STACKPop()));
	      return;
	      break;
	      }
	      EQ("cons"){
	      eval_aux(LOC(1));
	      eval_aux(LOC(2));
	      STACKPush(Cons(STACKPop(), STACKPop()));
	      return;
	      break;
	      }
	      EQ("define"){
	      arg1 = LOC(1);
	      EnvAlloc(arg1->word, Cons(MMWord("lambda"),Cdr(Cdr(list))), my_processor);
	      STACKPush(arg1);
	      return;
	      break;
	      }
	      EQ("beer"){
	      arg1 = LOC(1);
	      EnvAlloc(arg1->word, Cons(MMWord("macro"),Cdr(Cdr(list))), my_processor);
	      STACKPush(arg1);
	      return;
	      break;
	      }
	      EQ("cond"){
	      numero1 = ListLength(list);
	      for(i=1;i<numero1;i++)
	      {
	      arg1 = LOC(i);
	      eval_aux(Car(arg1));
	      if(STACKPop() == MMT())
	      {
	      eval_aux(Car(Cdr(arg1)));
	      return;
	      break;
	      }
	      }
	      STACKPush(MMNil());
	      return;
	      break;
	      }
	      EQ("state"){
	      cryo_debuger(my_processor);
	      STACKPush(NULL);
	      return;
	      break;
	      }
	      EQ("import"){
	      numero1 = ListLength(list);
	      eval_aux(LOC(1));
	      arg1 = STACKPop();
	      FileName(arg1->word,file);
	      if(numero1 == 3)
	      {
	      eval_aux(LOC(2));
	      arg1 = STACKPop();
	      if(arg1 == MMT()) import_file(file,1);
	      }else{
	      import_file(file,0);
	      }

	      STACKPush(NULL);
	      return;
	      break;
	      }
	      EQ("length"){
	      eval_aux(LOC(1));
	      STACKPush(MMInteger(ListLength(STACKPop())));
	      return;
	      break;
	      }
	      EQ("read"){
	      STACKPush(read());
	      return;
	      break;
	      }
	      EQ("print"){
	      numero1 = ListLength(list);
	      for(i = 1; i<numero1-1; i++)
	      {
	      eval_aux(LOC(i));
	      print_noNewLine(STACKPop());
	      }
	      eval_aux(LOC(i));
	      print(STACKPop());
	      STACKPush(NULL);
	      return;
	      break;
	      }
	      EQ("nth"){
	      eval_aux(LOC(2));
	      eval_aux(LOC(1));
	      arg1 = STACKPop();
	      STACKPush(nth(arg1->i, STACKPop()));
	      return;
	      break;
	      } 
	      EQ("let"){
	      arg2 = Car(Cdr(list));
	      numero1 = ListLength(Car(Cdr(list)));
	      EnvPUSH(my_processor);
	      for(i=0;i<numero1;i++)
	      {
	      eval_aux(Car(Cdr(nth(i,arg2))));
	      arg1 = Car(nth(i,arg2));
	      EnvAlloc(arg1->word,STACKPop(),my_processor);
	      }
	      numero1 = ListLength(list);
	      for(i = 2; i<numero1; i++)
	      {
	      STACKPop();
	      eval_aux(LOC(i));
	      }
	      EnvPOP(my_processor);
	      STACKPush(NULL);
	      return;
	      break;
	      }
	      EQ("cicle"){
	      numero1 = ListLength(list);
	      eval_aux(LOC(1));
	      for(;STACKPop() == MMT();)
	      {
	      for(i = 2; i<numero1; i++)
	      {
	      eval_aux(LOC(i));
	      STACKPop();
	      }
	      eval_aux(LOC(1));
	      }
	      return;
	      break;
	      }
	      EQ("trace"){
	      numero1 = ListLength(list);
	      arg1 = NULL;
	      if(numero1 == 3)
	      {
	      eval_aux(LOC(2));
	      arg1 = STACKPop();
	      if(arg1 == MMT()) inTraceTabEnabled();
	      }else{
	      inTraceTabDisable();
	      }
	      eval_aux(LOC(1));
	      inTraceEnabled();
	      eval_aux(STACKPop());
	      inTraceDisable();
	      if(arg1 == MMT()) inTraceTabDisable();
	      STACKPush(NULL);
	      return;
	      }
	      EQ("isInteger?")
	      {
	      eval_aux(LOC(1));
	      Is(TYPE_INTEIRO);
	      return;
	      }
	      EQ("isDouble?")
	      {
	      eval_aux(LOC(1));
	      Is(TYPE_DOUBLE);
	      return;
	      }
	      EQ("isWord?")
	      {
	      eval_aux(LOC(1));
	      Is(TYPE_WORD );
	      return;
	      }
	      EQ("isCons?")
	      {
	      eval_aux(LOC(1));
	      Is(TYPE_CONS);
	      return;
	      }
	      EQ("isCharacter?")
	      {
	      eval_aux(LOC(1));
	      Is(TYPE_CHAR);
	      return;
	      }
	      EQ("isString?")
	      {
	      eval_aux(LOC(1));
	      Is(TYPE_STRING);
	      return;
	      }*/
			
      ERRORThrow3("'", MMSymbolName(first), "' is not defined");
      
      return;
      break;
    case CFSYMBOL_TYPE:
      if(object == MMNil()){
	STACKPush(object);
	return;
      }
				
      if(object == MMT()){
	STACKPush(object);
	return;
      }

      ERRORThrow3("'", MMSymbolName(object), "' has no value");
    }


  return;
}
